/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       font.cpp
 * Author:     karooolek
 * Created on: 2009-03-24
 *
 **********************************************************************************************************************/

#include "font.h"

namespace mGameEngine
{
namespace Graphics
{

Font::Font() : _chars(), _dls(), _texture()
{
}

Font::~Font()
{
    // TODO remove characters
}

const FontCharacter &Font::getCharacter(uint16 c) const
{
    return _chars.find(c)->second;
}

void Font::addCharacter(uint16 c, const FontCharacter &character)
{
    // save character
    _chars[c] = character;

    // remove old display list
    uint &dl = _dls[c];
    if(dl)
    {
        glDeleteLists(dl, 1);
    }

    // create display list
    dl = glGenLists(1);
    glNewList(dl, GL_COMPILE);

    float w = fabs(character.size.x / character.size.y);

    glBegin(GL_QUADS);
        glTexCoord2fv(character.position);
        glVertex2fv(Vector2(0.0f, 0.0f));

        glTexCoord2fv(character.position + Vector2(character.size.x, 0.0f));
        glVertex2fv(Vector2(w, 0.0f));

        glTexCoord2fv(character.position + character.size);
        glVertex2fv(Vector2(w, 1.0f));

        glTexCoord2fv(character.position + Vector2(0.0f, character.size.y));
        glVertex2fv(Vector2(0.0f, 1.0f));
    glEnd();

    glTranslatef(w, 0.0f, 0.0f);

    glEndList();
}

void Font::removeCharacter(uint16 c)
{
    // remove character
    _chars.erase(c);

    // remove display list
    uint &dl = _dls[c];
    if(dl)
    {
        glDeleteLists(dl, 1);
    }
    _dls.erase(c);
}

float Font::calcTextWidth(const String &text) const
{
    // no text
    if(text.empty())
    {
        return 0.0f;
    }

    // calculate width
    float w = 0.0f;
    for(uint i = 0; i != text.length(); ++i)
    {
        char c = text[i];
        const Map<uint16, FontCharacter>::ConstIterator &it = _chars.find(c);
        if(it == _chars.end())
        {
            continue;
        }
        const FontCharacter &ch = it->second;
        w += fabs(ch.size.x / ch.size.y);
    }

    return w;
}

void Font::drawText(const String &text) const
{
    // font material
    static GPUProgram gpuProgram(
#include "font.vert"
        ,
#include "font.frag"
        , true);

    // no text
    if(text.empty())
    {
        return;
    }

    // map string to display lists id
    Vector<uint> dls;
    for(uint i = 0; i != text.length(); ++i)
    {
        char c = text[i];
        const Map<uint16, uint>::ConstIterator &it = _dls.find(c);
        if(it == _dls.end())
        {
            continue;
        }
        dls.push_back(it->second);
    }

    // create material
    gpuProgram.use();
    _texture->setFilteringMode(TEX_FILTER_TRILINEAR);
    gpuProgram.setTexture("texture", _texture, 0);

    // draw text
    glPushMatrix();
        glCallLists(dls.size(), GL_UNSIGNED_INT, &dls[0]);
    glPopMatrix();
}

}
}

